/*
 * This file is part of Applied Energistics 2.
 * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
 *
 * Applied Energistics 2 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Applied Energistics 2 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Applied Energistics 2.  If not, see <http://www.gnu.org/licenses/lgpl>.
 */

package appeng.block.misc;

import java.util.List;

import org.joml.Vector3f;

import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;

import appeng.api.orientation.BlockOrientation;
import appeng.api.orientation.IOrientationStrategy;
import appeng.api.orientation.OrientationStrategies;
import appeng.api.orientation.RelativeSide;
import appeng.api.util.AEAxisAlignedBB;
import appeng.block.AEBaseEntityBlock;
import appeng.blockentity.misc.ChargerBlockEntity;
import appeng.blockentity.misc.ChargerRecipes;
import appeng.client.render.effects.LightningArcParticleData;
import appeng.core.AEConfig;
import appeng.core.AppEngClient;
import appeng.util.Platform;

public class ChargerBlock extends AEBaseEntityBlock<ChargerBlockEntity> {

    public ChargerBlock() {
        super(metalProps().noOcclusion());
    }

    @Override
    public IOrientationStrategy getOrientationStrategy() {
        return OrientationStrategies.full();
    }

    @Override
    public int getLightBlock(BlockState state, BlockGetter level, BlockPos pos) {
        return 2; // FIXME Double check this (esp. value range)
    }

    @Override
    protected ItemInteractionResult useItemOn(ItemStack heldItem, BlockState state, Level level, BlockPos pos,
            Player player, InteractionHand hand, BlockHitResult hit) {
        if (level.getBlockEntity(pos) instanceof ChargerBlockEntity charger) {
            var inv = charger.getInternalInventory();
            var chargingItem = inv.getStackInSlot(0);
            if (chargingItem.isEmpty()) {
                if (ChargerRecipes.findRecipe(level, heldItem) != null || Platform.isChargeable(heldItem)) {
                    var toInsert = heldItem.split(1);
                    inv.setItemDirect(0, toInsert);
                    return ItemInteractionResult.sidedSuccess(level.isClientSide);
                }
            }
        }

        return super.useItemOn(heldItem, state, level, pos, player, hand, hit);
    }

    @Override
    protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player,
            BlockHitResult hitResult) {
        if (level.getBlockEntity(pos) instanceof ChargerBlockEntity charger) {
            var inv = charger.getInternalInventory();
            var chargingItem = inv.getStackInSlot(0);
            if (!chargingItem.isEmpty()) {
                inv.setItemDirect(0, ItemStack.EMPTY);
                Platform.spawnDrops(player.level(), charger.getBlockPos().relative(charger.getFront()),
                        List.of(chargingItem));
                return InteractionResult.sidedSuccess(level.isClientSide);
            }
        }

        return super.useWithoutItem(state, level, pos, player, hitResult);
    }

    @Override
    @OnlyIn(Dist.CLIENT)
    public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource r) {
        if (!AEConfig.instance().isEnableEffects()) {
            return;
        }

        var blockEntity = this.getBlockEntity(level, pos);
        if (blockEntity != null && blockEntity.isWorking()) {
            if (r.nextFloat() < 0.5) {
                return;
            }

            var rotation = BlockOrientation.get(blockEntity);

            for (int bolts = 0; bolts < 3; bolts++) {
                // Slightly offset the lightning arc on the x/z plane
                var xOff = Mth.randomBetween(r, -0.15f, 0.15f);
                var zOff = Mth.randomBetween(r, -0.15f, 0.15f);

                // Compute two points in the charger block. One at the bottom, and one on the top.
                // Account for the rotation while doing this.
                var center = new Vector3f(pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f);
                var origin = new Vector3f(xOff, -0.3f, zOff);
                origin.rotate(rotation.getQuaternion());
                origin.add(center);
                var target = new Vector3f(xOff, 0.3f, zOff);
                target.rotate(rotation.getQuaternion());
                target.add(center);

                // Split the arcs between arc coming from the top/bottom of the charger since it's symmetrical
                if (r.nextBoolean()) {
                    var tmp = target;
                    target = origin;
                    origin = tmp;
                }

                if (AppEngClient.instance().shouldAddParticles(r)) {
                    Minecraft.getInstance().particleEngine.createParticle(
                            new LightningArcParticleData(target),
                            origin.x(),
                            origin.y(),
                            origin.z(),
                            0.0, 0.0, 0.0);
                }
            }
        }
    }

    @Override
    public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
        var orientation = getOrientation(state);
        var up = orientation.getSide(RelativeSide.TOP);
        var forward = orientation.getSide(RelativeSide.FRONT);
        var twoPixels = 2.0 / 16.0;

        var bb = new AEAxisAlignedBB(twoPixels, twoPixels, twoPixels, 1.0 - twoPixels,
                1.0 - twoPixels, 1.0 - twoPixels);

        if (up.getStepX() != 0) {
            bb.minX = 0;
            bb.maxX = 1;
        }
        if (up.getStepY() != 0) {
            bb.minY = 0;
            bb.maxY = 1;
        }
        if (up.getStepZ() != 0) {
            bb.minZ = 0;
            bb.maxZ = 1;
        }

        switch (forward) {
            case DOWN -> bb.maxY = 1;
            case UP -> bb.minY = 0;
            case NORTH -> bb.maxZ = 1;
            case SOUTH -> bb.minZ = 0;
            case EAST -> bb.minX = 0;
            case WEST -> bb.maxX = 1;
            default -> {
            }
        }

        return Shapes.create(bb.getBoundingBox());
    }

    @Override
    public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos,
            CollisionContext context) {
        return Shapes.create(new AABB(0.0, 0.0, 0.0, 1.0, 1.0, 1.0));
    }

}
